package com.star.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.star.system.config.LocalAuthenticationService;
import com.star.system.dto.RoleSaveDTO;
import com.star.system.dto.RoleUpdateDTO;
import com.star.system.entity.Role;
import com.star.system.entity.User;
import com.star.system.enums.DefaultUser;
import com.star.system.mapper.RoleMapper;
import com.star.system.service.MenuService;
import com.star.system.service.RoleMenuService;
import com.star.system.service.RoleService;
import com.star.system.service.UserRoleService;
import com.star.system.struct.RoleStruct;
import com.star.system.vo.RoleDetailVO;
import com.star.system.vo.RolePageVO;
import com.star.system.vo.RoleSimpleVO;
import com.star.system.vo.UserRoleVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 角色 服务实现类
 *
 * @author yangzj
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    @Autowired
    private MenuService menuService;
    @Autowired
    private RoleMenuService roleMenuService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private LocalAuthenticationService localAuthenticationService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(RoleSaveDTO dto) {
        this.check(dto, null);
        Role Role = RoleStruct.INSTANCE.to(dto).setId(null);
        this.save(Role);
        // 保存角色菜单
        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            roleMenuService.saveBatch(Role.getId(), dto.getMenuIds());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(RoleUpdateDTO dto) {
        Long id = dto.getId();
        this.checkId(id);
        this.check(dto, id);
        this.update(RoleStruct.INSTANCE.to(dto), Wrappers.<Role>lambdaUpdate()
                .eq(Role::getId, id)
                .set(Role::getCode, dto.getCode())
                .set(Role::getRemark, dto.getRemark()));
        roleMenuService.updateBatch(id, dto.getMenuIds());
        // 刷新用户权限
        baseMapper.listUserByIds(Collections.singletonList(id)).forEach(localAuthenticationService::refresh);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteByIds(List<Long> ids) {
        ids.forEach(this::checkId);
        // 影响的用户
        List<User> users = baseMapper.listUserByIds(ids);
        // 角色用户
        userRoleService.deleteByRoleIds(ids);
        // 角色菜单
        roleMenuService.deleteByRoleIds(ids);
        this.removeByIds(ids);
        // 刷新小程序用户权限
        users.forEach(localAuthenticationService::refresh);
    }

    @Override
    public List<RolePageVO> listPage() {
        List<RolePageVO> list = baseMapper.listPage();
        list.forEach(c->c.setIsEdit(!DefaultUser.containsRoleId(c.getId())));
        return list;
    }

    /**
     * 检查id
     *
     * @param id id
     */
    private void checkId(Long id) {
        Assert.isFalse(DefaultUser.containsRoleId(id), "该角色为默认角色不能编辑");
    }

    @Override
    public RoleDetailVO getDetail(Long id) {
        return Assert.notNull(baseMapper.getDetail(id), "角色不存在")
                .setMenus(menuService.treeRole(id));
    }

    @Override
    public List<UserRoleVO> listUserRole(List<Long> userIds) {
        return baseMapper.listUserRole(userIds);
    }

    @Override
    public List<RoleSimpleVO> listAll() {
        return baseMapper.listAll();
    }

    /**
     * 检查
     *
     * @param dto dto
     */
    private void check(RoleSaveDTO dto, Long id) {
        boolean save = id == null;
        String name = dto.getName();
        Role role = save ? null : Assert.notNull(this.getById(id), "角色不存在");
        if (save || !Objects.equals(role.getName(), name)) {
            Assert.isTrue(this.lambdaQuery().eq(Role::getName, name).count() == 0
                    , "角色名称已存在");
        }
        String code = dto.getCode();
        boolean checkCode = code != null && (save || !Objects.equals(role.getCode(), code));
        if (checkCode) {
            Assert.isTrue(this.lambdaQuery().eq(Role::getCode, code).count() == 0
                    , "角色编码已存在");
        }
    }
}
